package tum0r.image;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

/**
 * 图片相似度检测类，可以new一个对象后直接调用getImageSimilarity方法并传入相关参数获取两张图片的相似度百分数
 * <p>
 * ClassName: ImageSimilarity
 * Author: tum0r
 * Time: 2020/4/11 10:51
 */
public class ImageSimilarity extends ImageBase {

    // 缩放后的图片大小为8x8
    private static final int ZOOM_WIDTH = 8;
    private static final int ZOOM_HEIGHT = 8;

    /**
     * 获取图片明暗结构指纹
     *
     * @param image 要获取指纹的图片BufferedImage对象
     * @return 图片指纹的字符串
     */
    public String getFingerPrint(BufferedImage image) {
        StringBuilder result = new StringBuilder();
        if (image != null) {
            int width = image.getWidth();
            int height = image.getHeight();
            long sum = 0;
            long count = 0;
            long average;

            // 获取图片灰度值总和，计算平均灰度值
            for (int x = image.getMinX(); x < width; x++) {
                for (int y = image.getMinY(); y < height; y++) {
                /*
                灰度值获取为负数，加上绝对值取正值
                灰度值获取为负数的原因为getRGB获取的实际上是ARGB的值，A为Alpha
                Alpha值默认为255，所以与RGB一起形成32位有符号整数
                */
                    sum += Math.abs(image.getRGB(x, y));
                    count++;
                }
            }
            average = sum / count;
            for (int x = 0; x < width; x++) {
                for (int y = 0; y < height; y++) {

                    // 当像素点灰度值大于等于平均灰度值时指纹为1，小于时指纹为0
                    if (Math.abs(image.getRGB(x, y)) >= average) {
                        result.append("1");
                    } else {
                        result.append("0");
                    }
                }
            }
        }
        return result.toString();
    }

    /**
     * 获取两个指纹字符串的汉明距离，即两个指纹字符串对应不同的位数
     *
     * @param fingerprint1 指纹字符串1
     * @param fingerprint2 指纹字符串2
     * @return 汉明距离
     */
    public int getHammingDistance(String fingerprint1, String fingerprint2) {
        int result = -1;
        if (fingerprint1 != null && fingerprint2 != null) {
            int length = fingerprint1.length();
            if (length == fingerprint2.length() && length > 0) {
                result = 0;
                char[] temp1 = fingerprint1.toCharArray();
                char[] temp2 = fingerprint2.toCharArray();
                for (int i = 0; i < length; i++) {
                    if (temp1[i] != temp2[i]) {
                        result++;
                    }
                }
            }
        }
        return result;
    }

    /**
     * 返回两个指纹字符串的相似度百分数
     *
     * @param fingerprint1 指纹字符串1
     * @param fingerprint2 指纹字符串2
     * @return 两个指纹字符串的相似度百分数，精确到小数点后2位
     */
    public double getSimilarity(String fingerprint1, String fingerprint2) {
        double result = 0.00;
        int hammingDistance = getHammingDistance(fingerprint1, fingerprint2);
        if (hammingDistance != -1) {
            int length = fingerprint1.length();
            result = ((length - hammingDistance) / (double) length) * 100;
        }
        return Double.parseDouble(String.format("%.2f", result));
    }

    @Override
    public BufferedImage handle(BufferedImage image) {
        return null;
    }

    /**
     * 获取两张图片相似度百分数
     *
     * @param imagePath1 图片1的路径
     * @param imagePath2 图片2的路径
     * @return 图片的相似度百分数，精确到小数点后2位
     * @throws IOException
     */
    public double getImageSimilarity(String imagePath1, String imagePath2) throws IOException {
        return getImageSimilarity(new File(imagePath1), new File(imagePath2));
    }

    /**
     * 获取两张图片相似度百分数
     *
     * @param imageFile1 图片1的文件对象
     * @param imageFile2 图片2的文件对象
     * @return 图片的相似度百分数，精确到小数点后2位
     * @throws IOException
     */
    public double getImageSimilarity(File imageFile1, File imageFile2) throws IOException {
        BufferedImage image1 = getImage(imageFile1);
        BufferedImage image2 = getImage(imageFile2);

        // 处理图片，先缩放，再灰度化
        image1 = gray(zoom(image1, ZOOM_WIDTH, ZOOM_HEIGHT));
        image2 = gray(zoom(image2, ZOOM_WIDTH, ZOOM_HEIGHT));

        // 获取图片明暗指纹并返回相似度百分数，精确到小数点后2位
        return getSimilarity(getFingerPrint(image1), getFingerPrint(image2));
    }
}
